
package com.jf.cloud.product.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.jf.cloud.api.constant.EsRenovationProductSortEnum;
import com.jf.cloud.api.feign.SearchSpuFeignClient;
import com.jf.cloud.api.leaf.feign.SegmentFeignClient;
import com.jf.cloud.api.order.feign.OrderCommFeignClient;
import com.jf.cloud.api.product.bo.SpuSimpleBO;
import com.jf.cloud.api.user.feign.UserFeignClient;
import com.jf.cloud.api.user.vo.UserApiVO;
import com.jf.cloud.api.vo.EsPageVO;
import com.jf.cloud.common.cache.util.RedisUtil;
import com.jf.cloud.common.constant.Constant;
import com.jf.cloud.common.constant.DistributedIdKey;
import com.jf.cloud.common.constant.StatusEnum;
import com.jf.cloud.common.database.dto.PageDTO;
import com.jf.cloud.common.database.vo.PageVO;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.i18n.I18nMessage;
import com.jf.cloud.common.mongodb.util.MongoPageUtil;
import com.jf.cloud.common.order.vo.OrderItemLangVO;
import com.jf.cloud.common.order.vo.OrderItemVO;
import com.jf.cloud.common.product.dto.ProductSearchDTO;
import com.jf.cloud.common.product.vo.SpuVO;
import com.jf.cloud.common.product.vo.search.SpuAdminVO;
import com.jf.cloud.common.response.ServerResponseEntity;
import com.jf.cloud.common.security.AuthUserContext;
import com.jf.cloud.common.util.Arith;
import com.jf.cloud.common.util.IpHelper;
import com.jf.cloud.product.bo.mongo.MongoSpuCommBO;
import com.jf.cloud.product.dto.SpuCommDTO;
import com.jf.cloud.product.model.SpuComm;
import com.jf.cloud.product.service.SpuCommService;
import com.jf.cloud.product.service.SpuExtensionService;
import com.jf.cloud.product.service.SpuService;
import com.jf.cloud.product.vo.SpuCommStatisticsStarVO;
import com.jf.cloud.product.vo.SpuCommStatisticsVO;
import com.jf.cloud.product.vo.SpuCommVO;
import com.jf.cloud.product.vo.mongo.MongoSpuCommStatisticsVO;
import io.seata.spring.annotation.GlobalTransactional;
import ma.glasnost.orika.MapperFacade;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 商品评论
 *
 * @author zz
 * @date 2021-01-11 13:47:33
 */
@Service
public class SpuCommServiceImpl implements SpuCommService {

    private final Logger logger = LoggerFactory.getLogger(SpuCommServiceImpl.class);

//    @Autowired
//    private SpuCommMapper spuCommMapper;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private SpuService spuService;

    @Autowired
    private OrderCommFeignClient orderCommFeignClient;

    @Autowired
    private SpuExtensionService spuExtensionService;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private MapperFacade mapperFacade;

    @Autowired
    private SegmentFeignClient segmentFeignClient;

    @Autowired
    private MongoPageUtil mongoPageUtil;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private SearchSpuFeignClient searchSpuFeignClient;
    public static final Logger log = LoggerFactory.getLogger(SpuCommServiceImpl.class);

    @Override
    public PageVO<SpuCommVO> page(PageDTO pageDTO, SpuCommDTO spuCommDTO) {
        PageVO<SpuCommVO> result = shopPage(pageDTO, spuCommDTO);
        if(CollUtil.isEmpty(result.getList())){
            return result;
        }
        List<Long> spuIds = result.getList().stream().map(SpuCommVO::getSpuId).collect(Collectors.toList());
        List<Long> userIds = result.getList().stream().map(SpuCommVO::getUserId).collect(Collectors.toList());
        List<SpuVO> spus = spuService.listSpuBySpuIds(spuIds);
        Map<Long, String> spuNameMap = spus.stream().collect(Collectors.toMap(SpuVO::getSpuId, SpuVO::getName));
        Map<Long, String> spuImgMap = spus.stream().collect(Collectors.toMap(SpuVO::getSpuId, SpuVO::getMainImgUrl));
        ServerResponseEntity<List<UserApiVO>> userResponse = userFeignClient.getUserByUserIds(userIds);
        Map<Long, String> nickNameMap = userResponse.getData().stream().collect(Collectors.toMap(UserApiVO::getUserId, UserApiVO::getNickName));
        for(SpuCommVO spuCommVO : result.getList()){
            spuCommVO.setSpuName(spuNameMap.get(spuCommVO.getSpuId()));
            spuCommVO.setNickName(nickNameMap.get(spuCommVO.getUserId()));
            spuCommVO.setSpuImg(spuImgMap.get(spuCommVO.getSpuId()));
        }
        return result;
    }

    @Override
    public SpuCommVO getBySpuCommId(Long spuCommId) {
        Criteria criteria = Criteria.where("_id").is(spuCommId);
//        return spuCommMapper.getBySpuCommId(spuCommId);

        SpuCommVO spuCommVO = getOne(criteria);
        return spuCommVO;
    }

    @Override
    public void save(MongoSpuCommBO mongoSpuCommBO) {
        // 设置id
        // 获取订单id列表
        ServerResponseEntity<Long> segmentIdResponse = segmentFeignClient.getSegmentId(DistributedIdKey.MALL4CLOUD_PRODUCT_COMM);
        if (segmentIdResponse.isFail()) {
            throw new LuckException(segmentIdResponse.getMsg());
        }
        if (StrUtil.isBlank(mongoSpuCommBO.getPics())) {
            mongoSpuCommBO.setPics(null);
        }
        mongoSpuCommBO.setSpuCommId(segmentIdResponse.getData());
        mongoTemplate.save(mongoSpuCommBO);
    }

    @Override
    public void update(SpuCommDTO spuComm) {
        spuComm.setStatus(1);
        spuComm.setReplyTime(new Date());
        MongoSpuCommBO mongoSpuCommBO = mapperFacade.map(spuComm, MongoSpuCommBO.class);
        mongoTemplate.save(mongoSpuCommBO);
    }

    @Override
    public void deleteById(Long spuCommId) {
        Criteria criteria = Criteria.where("_id").is(spuCommId);
        mongoTemplate.remove(new Query(criteria), MongoSpuCommBO.class);
//        spuCommMapper.deleteById(spuCommId);
    }

    @Override
    public PageVO<SpuCommVO> spuCommPage(PageDTO pageDTO, Long spuId, Integer evaluate) {
//        PageVO<SpuCommVO> spuCommPage = PageUtil.doPage(pageDTO, () -> spuCommMapper.spuCommPage(pageDTO, spuId, evaluate));
        PageVO<SpuCommVO> spuCommPage = spuPage(pageDTO, spuId, evaluate);
        if (CollectionUtil.isEmpty(spuCommPage.getList())) {
            return spuCommPage;
        }
        //
        Set<Long> userIds = new HashSet<>();
        Set<Long> orderItemIds = new HashSet<>();
        spuCommPage.getList().forEach(spuComm -> {
            orderItemIds.add(spuComm.getOrderItemId());
            // 非匿名评价
            if (!Objects.equals(spuComm.getIsAnonymous(), SpuComm.anonymous)) {
                userIds.add(spuComm.getUserId());
            }
        });
        ServerResponseEntity<List<OrderItemLangVO>> orderItemRes = orderCommFeignClient.listOrderItemLangByIds(new ArrayList<>(orderItemIds));
        ServerResponseEntity<List<UserApiVO>> userResponse = userFeignClient.getUserByUserIds(new ArrayList<>(userIds));
        Map<Long, UserApiVO> userMap = userResponse.getData().stream().collect(Collectors.toMap(UserApiVO::getUserId, u -> u));
        Map<Long, OrderItemLangVO> orderItemLangMap;
        if(CollUtil.isNotEmpty(orderItemRes.getData())) {
            orderItemLangMap = orderItemRes.getData().stream().collect(Collectors.toMap(OrderItemLangVO::getOrderItemId, o -> o));
        } else {
            orderItemLangMap = new HashMap<>();
        }
        spuCommPage.getList().forEach(spuComm -> {
            OrderItemLangVO orderItemLangVO = orderItemLangMap.get(spuComm.getOrderItemId());
            if (Objects.nonNull(orderItemLangVO)) {
                spuComm.setSkuName(orderItemLangVO.getSkuName());
            }
            if (!Objects.equals(spuComm.getIsAnonymous(), SpuComm.anonymous)) {
                UserApiVO userVO = userMap.get(spuComm.getUserId());
                if (Objects.nonNull(userVO)) {
                    spuComm.setNickName(userVO.getNickName());
                    spuComm.setUserPic(userVO.getPic());
                }else {
                    // 没有用户信息说明用户已注销
                    spuComm.setIsWriteOff(true);
                }
            }
        });
        return spuCommPage;
    }

    @Override
    public SpuCommStatisticsVO getProdCommDataBySpuId(Long spuId) {
//        SpuCommStatisticsVO spuCommStatistics = spuCommMapper.getProdCommDataBySpuId(spuId);
        MatchOperation matchOperation = Aggregation.match(Criteria.where("spuId").is(spuId).and("status").is(1));

        ProjectionOperation projectionOperation = Aggregation.project()
                .and(ConditionalOperators.when(Criteria.where("evaluate").in(1)).then(1).otherwise(0)).as("praiseNumber")
                .and(ConditionalOperators.when(Criteria.where("evaluate").in(2)).then(1).otherwise(0)).as("secondaryNumber")
                .and(ConditionalOperators.when(Criteria.where("evaluate").in(3)).then(1).otherwise(0)).as("negativeNumber")
                .and(ConditionalOperators.ifNull("pics").then(1)).as("picNumber");


        GroupOperation groupOperation = Aggregation.group()
                .count().as("number")
                .sum("praiseNumber").as("praiseNumber")
                .sum("secondaryNumber").as("secondaryNumber")
                .sum("negativeNumber").as("negativeNumber")
                .sum(ConditionalOperators.when(Criteria.where("picNumber").ne(1)).then(1).otherwise(0)).as("picNumber");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoSpuCommStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoSpuCommStatisticsVO.class,
                        matchOperation,
                        projectionOperation,
                        groupOperation
                );
        logger.info("构建getProdCommDataBySpuId()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoSpuCommStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoSpuCommStatisticsVO.class);
        // 取出最终结果
        List<MongoSpuCommStatisticsVO> list = aggregationResults.getMappedResults();
        SpuCommStatisticsVO spuCommStatistics;
        if (CollUtil.isEmpty(list)) {
            spuCommStatistics = new SpuCommStatisticsVO();
        } else {
            spuCommStatistics = mapperFacade.map(list.get(0), SpuCommStatisticsVO.class);
        }
        if (Objects.isNull(spuCommStatistics.getNumber())) {
            spuCommStatistics.setNumber(0);
        }
        if (Objects.isNull(spuCommStatistics.getPraiseNumber())) {
            spuCommStatistics.setPraiseNumber(0);
        }
        if (Objects.isNull(spuCommStatistics.getSecondaryNumber())) {
            spuCommStatistics.setSecondaryNumber(0);
        }
        if (Objects.isNull(spuCommStatistics.getNegativeNumber())) {
            spuCommStatistics.setNegativeNumber(0);
        }
        if (Objects.isNull(spuCommStatistics.getPositiveRating())) {
            spuCommStatistics.setPositiveRating(0D);
        }
        if (Objects.isNull(spuCommStatistics.getPicNumber())) {
            spuCommStatistics.setPicNumber(0);
        }

        //计算出好评率
        if(spuCommStatistics.getPraiseNumber() == 0 || spuCommStatistics.getNumber() == 0){
            spuCommStatistics.setPositiveRating(0.0);
            return spuCommStatistics;
        }
        spuCommStatistics.setPositiveRating(Arith.div(Arith.mul(spuCommStatistics.getPraiseNumber() , 100),spuCommStatistics.getNumber(),2));
        return spuCommStatistics;
    }

    @Override
    public SpuCommStatisticsStarVO getProdCommDataByStar(Long spuId) {

//        SpuCommStatisticsStarVO spuCommStatisticsStarVO = spuCommMapper.getProdCommDataByStar(spuId);

        MatchOperation matchOperation = Aggregation.match(Criteria.where("spuId").is(spuId).and("status").is(1));

        ProjectionOperation projectionOperation = Aggregation.project("actualTotal","platformAmount","refundStatus")
                .and(ConditionalOperators.when(Criteria.where("score").in(5)).then(1).otherwise(0)).as("fiveStarsNumber")
                .and(ConditionalOperators.when(Criteria.where("score").in(4)).then(1).otherwise(0)).as("fourStarsNumber")
                .and(ConditionalOperators.when(Criteria.where("score").in(3)).then(1).otherwise(0)).as("threeStarsNumber")
                .and(ConditionalOperators.when(Criteria.where("score").in(2)).then(1).otherwise(0)).as("twoStarsNumber")
                .and(ConditionalOperators.when(Criteria.where("score").in(1)).then(1).otherwise(0)).as("oneStarsNumber");

        GroupOperation groupOperation = Aggregation.group()
                .sum("fiveStarsNumber").as("fiveStarsNumber")
                .sum("fourStarsNumber").as("fourStarsNumber")
                .sum("threeStarsNumber").as("threeStarsNumber")
                .sum("twoStarsNumber").as("twoStarsNumber")
                .sum("oneStarsNumber").as("oneStarsNumber");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoSpuCommStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoSpuCommStatisticsVO.class,
                        matchOperation,
                        projectionOperation,
                        groupOperation
                );
        logger.info("构建getProdCommDataByStar()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoSpuCommStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoSpuCommStatisticsVO.class);
        // 取出最终结果
        List<MongoSpuCommStatisticsVO> list = aggregationResults.getMappedResults();
        SpuCommStatisticsStarVO spuCommStatisticsStarVO;
        if (CollUtil.isEmpty(list)) {
            spuCommStatisticsStarVO = new SpuCommStatisticsStarVO();
        } else {
            spuCommStatisticsStarVO = mapperFacade.map(list.get(0), SpuCommStatisticsStarVO.class);
        }
        if (Objects.isNull(spuCommStatisticsStarVO.getFiveStarsNumber())) {
            spuCommStatisticsStarVO.setFiveStarsNumber(0);
        }
        if (Objects.isNull(spuCommStatisticsStarVO.getTwoStarsNumber())) {
            spuCommStatisticsStarVO.setTwoStarsNumber(0);
        }
        if (Objects.isNull(spuCommStatisticsStarVO.getThreeStarsNumber())) {
            spuCommStatisticsStarVO.setThreeStarsNumber(0);
        }
        if (Objects.isNull(spuCommStatisticsStarVO.getFourStarsNumber())) {
            spuCommStatisticsStarVO.setFourStarsNumber(0);
        }
        if (Objects.isNull(spuCommStatisticsStarVO.getOneStarsNumber())) {
            spuCommStatisticsStarVO.setOneStarsNumber(0);
        }
        return spuCommStatisticsStarVO;
    }

    @Override
    public List<Long> getCommNumRankShopIdAndTime(String key, Long shopId, Integer dayNum, Integer expireTime, Integer esRenovationSpuSort, Long primaryCategoryId) {
        List<Long> spuIds = RedisUtil.getListRange(key,0L,10L);

        if (CollectionUtil.isNotEmpty(spuIds)) {
            return spuIds;
        }
        // 加锁，防止缓存击穿
        RLock rLock = redissonClient.getLock("redisson_lock:comm_num_rank:" + key);
        try {
            int lockWait = 30;
            if (rLock.tryLock(lockWait, lockWait, TimeUnit.SECONDS)) {
                spuIds = RedisUtil.getListRange(key,0L,10L);
                if (CollectionUtil.isEmpty(spuIds)) {
                    List<MongoSpuCommStatisticsVO> list = listMongoSpuCommStatistics(shopId, dayNum, esRenovationSpuSort);
                    spuIds = list.stream().map(MongoSpuCommStatisticsVO::getSpuId).collect(Collectors.toList());
                    if (CollUtil.isEmpty(spuIds) || Objects.isNull(primaryCategoryId)) {
                        return spuIds;
                    }
                    handleCategoryId(primaryCategoryId, spuIds);
                }
            } else {
                throw new LuckException("网络繁忙，请稍后再试");
            }
        } catch (InterruptedException e) {
            log.error("InterruptedException:", e);
        } finally {
            try {
                if (rLock.isLocked()) {
                    rLock.unlock();
                }
            } catch (Exception e) {
                log.error("Exception:", e);
            }
        }
        return spuIds;
    }

    private void handleCategoryId(Long primaryCategoryId, List<Long> spuIds) {
        ProductSearchDTO productSearchDTO = new ProductSearchDTO();
        productSearchDTO.setPageSize(PageDTO.MAX_PAGE_SIZE);
        productSearchDTO.setPageNum(PageDTO.DEFAULT_PAGE_NUM);
        productSearchDTO.setSpuIds(spuIds);
        ServerResponseEntity<EsPageVO<SpuAdminVO>> searchResponse = searchSpuFeignClient.adminPage(productSearchDTO);
        if (searchResponse.isFail()) {
            throw new LuckException(searchResponse.getMsg());
        }
        List<SpuAdminVO> spuList = searchResponse.getData().getList();
        if (CollUtil.isNotEmpty(spuList)) {
            Map<Long, Long> categoryMap = spuList.stream().collect(Collectors.toMap(SpuAdminVO::getSpuId, SpuAdminVO::getPrimaryCategoryId));
            Iterator<Long> iterator = spuIds.iterator();
            while (iterator.hasNext()) {
                Long spuId = iterator.next();
                Long categoryId = categoryMap.get(spuId);
                if (!Objects.equals(categoryId, primaryCategoryId)) {
                    iterator.remove();
                }
            }
        }
    }

    private List<MongoSpuCommStatisticsVO> listMongoSpuCommStatistics(Long shopId, Integer dayNum, Integer esRenovationSpuSort) {
        Date time = null;
        if(dayNum != 0) {
            time = DateUtil.offsetDay(new DateTime(), -dayNum);
        }
        // 过滤
        Criteria criteria = null;
        if(Objects.nonNull(time)) {
            if (Objects.isNull(shopId) || Objects.equals(shopId, 0L)) {
                criteria = Criteria.where("createTime").gt(time);
            } else {
                criteria = Criteria.where("createTime").gt(time).and("shopId").is(shopId);
            }
        }else{
            if (Objects.nonNull(shopId) && !Objects.equals(shopId, 0L)) {
                criteria = Criteria.where("shopId").is(shopId);
            }
        }
        // 分组
        GroupOperation spuIdGroupOperation = Aggregation.group("spuId").count().as("number");
        ProjectionOperation projectionOperation = Aggregation.project("number")
                .and("_id").as("spuId");
        Sort.Direction sort = null;
        if(Objects.equals(esRenovationSpuSort, EsRenovationProductSortEnum.COMMENT_NUM_DESC.value())){
            sort = Sort.Direction.DESC;
        }else{
            sort = Sort.Direction.ASC;
        }
        SortOperation sortOperation = Aggregation.sort(sort, "number");
        TypedAggregation<MongoSpuCommStatisticsVO> typedAggregation = null;
        if(Objects.nonNull(criteria)) {
            // 按顺序组合每一个聚合步骤
            typedAggregation =
                    Aggregation.newAggregation(
                            MongoSpuCommStatisticsVO.class,
                            Aggregation.match(criteria),
                            spuIdGroupOperation,
                            projectionOperation,
                            sortOperation,
                            Aggregation.limit(1000)
                    );
        }else{
            // 按顺序组合每一个聚合步骤
            typedAggregation =
                    Aggregation.newAggregation(
                            MongoSpuCommStatisticsVO.class,
                            spuIdGroupOperation,
                            sortOperation,
                            projectionOperation,
                            Aggregation.limit(1000)
                    );
        }
        logger.info("getTradeRetained()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoSpuCommStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoSpuCommStatisticsVO.class);
        // 取出最终结果
        List<MongoSpuCommStatisticsVO> list = aggregationResults.getMappedResults();
        return CollectionUtil.isEmpty(list) ? new ArrayList<>() : list;
    }

    @Override
    public PageVO<SpuCommVO> spuCommPageByUserId(PageDTO pageDTO, Long userId) {
//        PageVO<SpuCommVO> page = PageUtil.doPage(pageDTO, () -> spuCommMapper.spuCommListByUserId(userId));
        PageVO<SpuCommVO> page = userPage(pageDTO, userId);
        ServerResponseEntity<UserApiVO> userResponse = userFeignClient.getUserData(userId);
        UserApiVO userApiVO = userResponse.getData();
        for (SpuCommVO spuCommVO : page.getList()) {
            spuCommVO.setNickName(userApiVO.getNickName());
            spuCommVO.setPics(userApiVO.getPic());
        }
        return page;
    }

    @Override
    public List<SpuCommVO> getSpuCommByOrderItemId(List<Long> orderItemIds, Long userId) {
        Criteria criteria = Criteria.where("userId").is(userId).and("orderItemId").in(orderItemIds);
        List<SpuCommVO> spuCommVOList = getList(criteria);
//        SpuCommVO spuCommVO = spuCommMapper.getSpuCommByOrderItemId(orderItemId, userId);
        ServerResponseEntity<List<OrderItemVO>> orderItemVOList = orderCommFeignClient.getListByOrderItemId(orderItemIds);
        if (!orderItemVOList.isSuccess()) {
            throw new LuckException("获取订单信息出错");
        }
        Map<Long, OrderItemVO> itemVOMap = orderItemVOList.getData().stream().collect(Collectors.toMap(OrderItemVO::getOrderItemId, s -> s));
        List<Long> spuIds = spuCommVOList.stream().map(SpuCommVO::getSpuId).collect(Collectors.toList());
        List<SpuVO> spuVOList = spuService.getBySpuIds(spuIds);
        Map<Long, String> spuMap = spuVOList.stream().distinct().collect(Collectors.toMap(SpuVO::getSpuId, SpuVO::getMainImgUrl));
        for (SpuCommVO spuCommVO : spuCommVOList) {
            spuCommVO.setSpuImg(spuMap.get(spuCommVO.getSpuId()));
            spuCommVO.setSkuName(itemVOMap.get(spuCommVO.getOrderItemId()).getSkuName());
            spuCommVO.setTransactionTime(itemVOMap.get(spuCommVO.getOrderItemId()).getCreateTime());
        }
        return spuCommVOList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @GlobalTransactional(rollbackFor = Exception.class)
    public void comm(SpuCommDTO spuCommDTO) {
        ServerResponseEntity<OrderItemVO> orderItemResponse = orderCommFeignClient.getByOrderItemId(spuCommDTO.getOrderItemId());
        if (!orderItemResponse.isSuccess()) {
            throw new LuckException(orderItemResponse.getMsg());
        }
        OrderItemVO orderItemVO = orderItemResponse.getData();
        // refundStatus = RefundStatusEnum.DISAGREE.value()
        int refundStatus = 4;
        if (Objects.nonNull(orderItemVO.getRefundStatus()) && !Objects.equals(refundStatus,orderItemVO.getRefundStatus())) {
            throw new LuckException("[" +orderItemVO.getSpuName()+"]" +"该商品正在进行售后处理，不能进行评价!");
        }
        Long spuId = getSpuIdByOrderItemId(spuCommDTO.getOrderItemId());
        // 保存商品评论
        MongoSpuCommBO spuComm = saveSpuComm(spuId, spuCommDTO);
        // 更新商品扩展信息表商品评价数量
        spuExtensionService.changeCommentNum(spuId, 1);

        // 更新订单项评论状态
        orderItemVO.setUpdateTime(spuComm.getCreateTime());
        orderItemVO.setCommTime(spuComm.getCreateTime());
        orderItemVO.setIsComm(Constant.IS_COMM);
        orderItemVO.setUpdateTime(new Date());

        orderCommFeignClient.updateOrderItemComm(orderItemVO);
    }

    @Override
    public int countGoodReview(Long spuId) {
        // 过滤
        Criteria criteria = Criteria.where("spuId").is(spuId).and("evaluate").is(1).and("status").is(1);
        MatchOperation matchOperation = Aggregation.match(criteria);
        // 分组计算数量
        GroupOperation groupOperation = Aggregation.group()
                .count().as("number");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoSpuCommStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoSpuCommStatisticsVO.class,
                        matchOperation,
                        groupOperation
                );
        logger.info("构建countGoodReview()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoSpuCommStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoSpuCommStatisticsVO.class);
        // 取出最终结果
        List<MongoSpuCommStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return 0;
        }
        Integer number = list.get(0).getNumber();
        if (Objects.isNull(number)) {
            number = 0;
        }
        return number;
    }

    /**
     * 获取评论的spuId，同时校验评论的
     * @param orderItemId
     * @return
     */
    private Long getSpuIdByOrderItemId(Long orderItemId) {
        ServerResponseEntity<Long> orderResponse  = orderCommFeignClient.getSpuIdByOrderItemId(orderItemId);
        if (orderResponse.isFail()) {
            throw new LuckException(orderResponse.getMsg());
        }
        Long spuId = null;
        try {
            spuId = Long.valueOf(orderResponse.getData().toString());
        } catch (Exception e) {
            throw new LuckException("评论的商品数据有误，请刷新后重试");
        }
        return spuId;
    }

    /**
     * 保存商品评论信息
     * @param spuId
     * @param spuCommDTO
     */
    private MongoSpuCommBO saveSpuComm(Long spuId, SpuCommDTO spuCommDTO) {
        if(StrUtil.length(spuCommDTO.getContent()) > Constant.MAX_FIELD_LIMIT){
            // 截取字符串
            spuCommDTO.setContent(StrUtil.subWithLength(spuCommDTO.getContent(),0,500));
        }
        if (StrUtil.isBlank(spuCommDTO.getPics())){
            spuCommDTO.setPics(null);
        }
        //查询是否已经评论
        ServerResponseEntity<OrderItemVO> byOrderItemId = orderCommFeignClient.getByOrderItemId(spuCommDTO.getOrderItemId());
        if (Objects.nonNull(byOrderItemId.getData())){
            OrderItemVO data = byOrderItemId.getData();
            Criteria criteria = Criteria.where("userId").is(data.getUserId()).and("orderItemId").is(spuCommDTO.getOrderItemId()).and("status").is(1);
            SpuCommVO spuCommVO = getOne(criteria);
            if ( Objects.nonNull(spuCommVO)) {
                throw new LuckException("该商品已评论");
            }
        }

        SpuVO spuVO = spuService.getBySpuId(spuId);
        // 插入评论
        MongoSpuCommBO spuComm = new MongoSpuCommBO();
        spuComm.setShopId(spuVO.getShopId());
        spuComm.setSpuId(spuId);
        spuComm.setOrderItemId(spuCommDTO.getOrderItemId());
        spuComm.setUserId(AuthUserContext.get().getUserId());
        spuComm.setScore(spuCommDTO.getScore());
        spuComm.setContent(spuCommDTO.getContent());
        spuComm.setPics(spuCommDTO.getPics());
        spuComm.setIsAnonymous(spuCommDTO.getIsAnonymous());
        spuComm.setCreateTime(new Date());
        // 平台不需要控评
        spuComm.setStatus(StatusEnum.ENABLE.value());
        spuComm.setPostip(IpHelper.getIpAddr());
        if (Objects.nonNull(spuComm.getScore()) && spuComm.getScore() > Constant.MEDIUM_RATING) {
            spuComm.setEvaluate(1);
        } else if (Objects.nonNull(spuComm.getScore()) && spuComm.getScore().equals(Constant.MEDIUM_RATING)) {
            spuComm.setEvaluate(2);
        } else if (Objects.nonNull(spuComm.getScore()) && spuComm.getScore() < Constant.MEDIUM_RATING) {
            spuComm.setEvaluate(3);
        }
        save(spuComm);
        return spuComm;
    }
    private PageVO<SpuCommVO> spuPage(PageDTO pageDTO, Long spuId, Integer evaluate) {
        Criteria criteria = Criteria.where("spuId").is(spuId).and("status").is(1);
        boolean writtenComm = Objects.nonNull(evaluate) && !Objects.equals(evaluate,  0) && !Objects.equals(evaluate, 4);
        // 文字评论
        if (writtenComm) {
            criteria.and("evaluate").is(evaluate);
        }
        // 图片评论
        else if (Objects.equals(evaluate,   Constant.FOUR)) {
            criteria.and("pics").exists(true);
        }
        Query query = new Query(criteria);
        query.with(Sort.by(Sort.Direction.DESC,"createTime"));
        return page(pageDTO, query);
    }

    private PageVO<SpuCommVO> shopPage(PageDTO pageDTO, SpuCommDTO spuCommDTO) {

        Criteria criteria = Criteria.where("shopId").is(spuCommDTO.getShopId()).and("status").is(1);

        //根据商品名称搜索
        if(StrUtil.isNotBlank(spuCommDTO.getSpuName())){
            SpuSimpleBO spuSimpleBO = new SpuSimpleBO();
            spuSimpleBO.setLang(I18nMessage.getLang());
            spuSimpleBO.setShopId(AuthUserContext.get().getTenantId());
            spuSimpleBO.setSpuName(spuCommDTO.getSpuName());
            List<SpuSimpleBO>  spuList = spuService.listSimple(spuSimpleBO);
            List<Long> spuIds = new ArrayList<>();
            if(CollUtil.isNotEmpty(spuList)){
                spuIds = spuList.stream().map(SpuSimpleBO::getSpuId).collect(Collectors.toList());
            }
            criteria.and("spuId").in(spuIds);
        }

        //根据用户昵称搜索
        if(StrUtil.isNotBlank(spuCommDTO.getNickName())){
            UserApiVO userApiVO = new UserApiVO();
            userApiVO.setNickName(spuCommDTO.getNickName());
            ServerResponseEntity<List<UserApiVO>> userResponse = userFeignClient.getUserByParam(userApiVO);
            List<UserApiVO> users  = userResponse.getData();
            List<Long> userIds = new ArrayList<>();
            if (CollUtil.isNotEmpty(users)){
                userIds = users.stream().map(UserApiVO::getUserId).collect(Collectors.toList());
            }
            criteria.and("userId").in(userIds);
        }


        if(Objects.nonNull(spuCommDTO.getCreateStartTime())){
            criteria.and("createTime").gt(spuCommDTO.getCreateStartTime()).lt(spuCommDTO.getCreateEndTime());
        }
        if (Objects.nonNull(spuCommDTO.getReplyStartTime())){
            criteria.and("replyTime").gt(spuCommDTO.getReplyStartTime()).lt(spuCommDTO.getReplyEndTime());
        }

        Query query = new Query(criteria);
        query.with(Sort.by(Sort.Direction.DESC,"createTime"));
        return page(pageDTO, query);
    }

    private PageVO<SpuCommVO> userPage(PageDTO pageDTO, Long userId) {
        Criteria criteria = Criteria.where("userId").is(userId);
        return page(pageDTO, new Query(criteria));
    }

    private PageVO<SpuCommVO> page(PageDTO pageDTO, Query query) {
        PageVO<MongoSpuCommBO> mongoSpuCommBOPageVO = mongoPageUtil.doPage(MongoSpuCommBO.class, pageDTO, query);
        PageVO<SpuCommVO> pageVO = new PageVO<>();
        pageVO.setPages(mongoSpuCommBOPageVO.getPages());
        pageVO.setTotal(mongoSpuCommBOPageVO.getTotal());
        pageVO.setList(mapperFacade.mapAsList(mongoSpuCommBOPageVO.getList(), SpuCommVO.class));
        return pageVO;
    }

    private SpuCommVO getOne(Criteria criteria) {
        List<MongoSpuCommBO> list = mongoTemplate.find(new Query(criteria), MongoSpuCommBO.class);
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        SpuCommVO spuCommVO = mapperFacade.map(list.get(0), SpuCommVO.class);
        return spuCommVO;
    }

    private List<SpuCommVO> getList(Criteria criteria) {
        List<MongoSpuCommBO> list = mongoTemplate.find(new Query(criteria), MongoSpuCommBO.class);
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        List<SpuCommVO> spuCommVOList = new ArrayList<>();
        for (MongoSpuCommBO mongoSpuCommBO : list) {
            SpuCommVO spuCommVO = mapperFacade.map(mongoSpuCommBO, SpuCommVO.class);
            spuCommVOList.add(spuCommVO);
        }
        return spuCommVOList;
    }
}
